package com.pernix.authen.network;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;

import net.jradius.util.RadiusRandom;

import com.pernix.authen.HashingProvider;
import com.pernix.authen.MSCHAP;
import com.pernix.authen.util.DisplayUtil;
import com.pernix.log.ConsoleLogging;
import com.pernix.log.ILog;

public class AuthenClient {
	private ILog logger;
	private String servername;
	private int port;
	private ObjectInputStream sInput; // to read from the socket
	private ObjectOutputStream sOutput; // to write on the socket
	private Socket socket;
	private String username;
	private String password;
	private byte[] serverChallenge;
	private byte[] peerChallenge;
	private byte[] challengeResponse;
	private byte[] authenResponse;
	private boolean authenResult=false;

	public AuthenClient(ILog logger, String servername, int port) {
		this.logger = logger;
		this.servername = servername;
		this.port = port;
	}

	private void sendMessage(AuthenMessage authenMessage) {
		try {
			sOutput.writeObject(authenMessage);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private class AuthenClientThread extends Thread {
		private boolean finished = false;

		@Override
		public void run() {
			while (!Thread.currentThread().isInterrupted()) {
				AuthenMessage authenMessage = null;
				try {
					authenMessage = (AuthenMessage) sInput.readObject();
				} catch (ClassNotFoundException | IOException e) {
					logger.Error(e.getMessage() + "\n" + e.getStackTrace());
					Thread.currentThread().interrupt();
				}
				if (authenMessage != null) {
					switch (authenMessage.authenMessageType) {
					case RESPONSE_CHALLENGE:
						logger.Debug(" Step 03: Receive Response Challenge");
						serverChallenge = authenMessage.serverChallenge;
						String salt = authenMessage.password;
						try {
							password = HashingProvider.hashSHA256WithSalt(password, salt);
						} catch (NoSuchAlgorithmException | IOException e) {
							e.printStackTrace();
						}
						peerChallenge = RadiusRandom.getBytes(16); 
						logger.Debug(" Step 03: [16byte] Server Challenge: " + DisplayUtil.print(serverChallenge));
						logger.Debug(" Step 03: Generate [16byte] Peer Challenge: " + DisplayUtil.print(peerChallenge));
						challengeResponse = MSCHAP.doMSCHAPv2(username.getBytes(), password.getBytes(), authenMessage.serverChallenge, peerChallenge);
						logger.Debug(" Step 03: [50 Byte] ChallengeResponse" + DisplayUtil.print(challengeResponse));
						logger.Debug(" Step 03: Send ChallengeResponse to Server");
						sendMessage(new AuthenMessage(AuthenMessageType.REQUSET_AUTHEN, challengeResponse, peerChallenge, username, password));
						break;
					case RESPONSE_AUTHEN:
						authenResponse = authenMessage.serverChallenge;
						logger.Debug(" Step 05: Receive Response Authen " + DisplayUtil.print(authenResponse));
						byte[] authenResComputeAtClient = MSCHAP.getAuthenResponse(serverChallenge, peerChallenge, username.getBytes(), password.getBytes());
						authenResult =  Arrays.equals(authenResComputeAtClient, authenResponse);
						logger.Debug(" Step 05: Verify Response Authen result is " + authenResult);
						Thread.currentThread().interrupt();
						break;
					default:
						break;
					}
				}
			}
			System.out.println("interrupt");
			setFinished(true);
		}
		public boolean isFinished() {
			return finished;
		}

		public void setFinished(boolean finished) {
			this.finished = finished;
		}
	}

	public boolean Authen(String username, String password) {
		try {
			this.username = username;
			this.password = password;
			socket = new Socket(servername, port);
			sInput = new ObjectInputStream(socket.getInputStream());
			sOutput = new ObjectOutputStream(socket.getOutputStream());
			logger.Debug(" Step 01: Send Request Authen to Authentication Server");
			sendMessage(new AuthenMessage(AuthenMessageType.REQUEST_CHALLENGE, username));
		} catch (IOException e) {
			e.printStackTrace();
		}
		AuthenClientThread authenThread = new AuthenClientThread();
		authenThread.start();
		while (!authenThread.isFinished()) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		}
		return authenResult;
	}

	public static void main(String[] args) {
		AuthenClient client = new AuthenClient(new ConsoleLogging(),
				"localhost", 1812);
		SimpleDateFormat formatDate = new SimpleDateFormat("hh:mm:ss");
		System.out.println(formatDate.format(new Date()));
		client.Authen("Keaw", "kkk");
		System.out.println(formatDate.format(new Date()));
		System.out.println("end.");
	}
}
